package br.com.pontoclass.main;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

import com.google.api.client.util.Base64;

/**
 * @author JavaDigest
 * 
 */
public class CredentialsEncrypter {

    private static final String DEFAULT_CHARSET_NAME = "UTF-8";
    public static final String ALGORITHM = "RSA";

    /**
     * Generate key which contains a pair of private and public key using 1024
     * bytes. Store the set of keys in Prvate.key and Public.key files.
     * 
     * @throws NoSuchAlgorithmException
     * @throws IOException
     * @throws FileNotFoundException
     */
    public static void generateKey(String privateKFile, String publicKFile) {
        try {
            final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
            keyGen.initialize(1024);
            final KeyPair key = keyGen.generateKeyPair();

            File privateKeyFile = new File(privateKFile);
            File publicKeyFile = new File(publicKFile);
            // Create files to store public and private key
            if (!(privateKeyFile.exists() && privateKeyFile.isFile())) {
                privateKeyFile.getParentFile().mkdirs();
            }
            privateKeyFile.createNewFile();
            if (!(publicKeyFile.exists() && publicKeyFile.isFile())) {
                publicKeyFile.getParentFile().mkdirs();
            }
            publicKeyFile.createNewFile();
            // Saving the Public key in a file
            ObjectOutputStream publicKeyOS = new ObjectOutputStream(new FileOutputStream(publicKeyFile));
            publicKeyOS.writeObject(key.getPublic());
            publicKeyOS.close();
            // Saving the Private key in a file
            ObjectOutputStream privateKeyOS = new ObjectOutputStream(new FileOutputStream(privateKeyFile));
            privateKeyOS.writeObject(key.getPrivate());
            privateKeyOS.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * The method checks if the pair of public and private key has been generated.
     * 
     * @return flag indicating if the pair of keys were generated.
     */
    public static boolean areKeysPresent(String privateKFile, String publicKFile) {

        File privateKey = new File(privateKFile);
        File publicKey = new File(publicKFile);

        if (privateKey.exists() && publicKey.exists()) {
            return true;
        }
        return false;
    }

    /**
     * Encrypt the plain text using public key.
     * 
     * @param text
     *          : original plain text
     * @param key
     *          :The public key
     * @return Encrypted text
     * @throws java.lang.Exception
     */
    public static byte[] encrypt(String text, PublicKey key) {
        byte[] cipherText = null;
        try {
            // get an RSA cipher object and print the provider
            final Cipher cipher = Cipher.getInstance(ALGORITHM);
            // encrypt the plain text using the public key
            cipher.init(Cipher.ENCRYPT_MODE, key);
            cipherText = cipher.doFinal(text.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cipherText;
    }

    /**
     * Decrypt text using private key.
     * 
     * @param text
     *          :encrypted text
     * @param key
     *          :The private key
     * @return plain text
     * @throws java.lang.Exception
     */
    public static String decrypt(byte[] text, PrivateKey key) {
        byte[] dectyptedText = null;
        try {
            // get an RSA cipher object and print the provider
            final Cipher cipher = Cipher.getInstance(ALGORITHM);

            // decrypt the text using the private key
            cipher.init(Cipher.DECRYPT_MODE, key);
            dectyptedText = cipher.doFinal(text);

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return new String(dectyptedText, Charset.forName(DEFAULT_CHARSET_NAME));
    }

    /**
     * Test the EncryptionUtil
     * @throws IOException 
     * @throws FileNotFoundException 
     * @throws ClassNotFoundException 
     */
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        // Check if the pair of keys are present else generate those.
        if (!areKeysPresent(args[1], args[2])) {
            // Method generates a pair of keys using the RSA algorithm and stores it
            // in their respective files
            generateKey(args[1], args[2]);
        }
        try (
                ObjectInputStream privateStream = new ObjectInputStream(new FileInputStream(args[1]));
                ObjectInputStream publicStream = new ObjectInputStream(new FileInputStream(args[2]));
                PrintWriter tbw = new PrintWriter(new File(args[0]));
                
        ){
            if(args.length > 4) {
                tbw.println();
                final PublicKey publicKey = (PublicKey) publicStream.readObject();
                for(int i = 3; i < args.length; i+=2) {
                    String k = args[i],
                           v = args[i+1];
                    
                    v = new String(Base64.encodeBase64(encrypt(v, publicKey)), Charset.forName(DEFAULT_CHARSET_NAME));
                    tbw.append(String.format("%s=%s", k, v));
                    tbw.println();
                }
                tbw.flush();
            }
        }
    }
}